#include "mcl/rt.h"
#include "mcl/code.h"
#include "mcl/da.h"
#include "mcl/log.h"
#include "mcl/sym.h"
#include "mcl/val.h"

static struct lvalue VAL_NIL = {.type = MC_NIL};

static struct env *new_env(struct env *parent)
{
	struct env *x = (struct env *)malloc(sizeof(struct env));
	x->binds = (struct da_vbind)DA_INIT;
	x->parent = parent;
	return x;
}

void env_bind(struct env *env, struct dstr name, struct lvalue *val)
{
	struct vbind bind = {.name = name, .val = val};
	DA_APPEND(env->binds, bind);
}

struct lvalue *env_find(struct env *env, struct dstr *name)
{
	while (env != NULL) {
		for (usize i = env->binds.len; i >= 1; i--) {
			if (dstr_eq(&env->binds.data[i - 1].name, name)) {
				return env->binds.data[i - 1].val;
			}
		}
		env = env->parent;
	}

	logerror("undefined symbol: %.*s", (int)name->len, name->data);

	return NULL;
}

// binds 是一个绑定列表，在 env 环境下求值这个绑定列表，将产生的绑定写到新的
// env_out 中。
static enum code eval_bind(
	struct runtime *rt,
	struct env *env,
	struct slice *binds,
	struct env **env_out)
{
	*env_out = new_env(env);

	if (binds->len % 2 != 0) {
		logerror("length of binds should be a multiple of 2");
		return CODE_OOPS;
	}

	for (usize i = 0; i < binds->len; i += 2) {
		if (binds->data[i].type != MC_SYM) {
			logerror("binds need a symbol at first");
			return CODE_OOPS;
		}

		struct lvalue *v = eval(rt, env, &binds->data[i + 1]);
		env_bind(*env_out, binds->data[i].via.sym, v);
	}

	return CODE_OK;
}

// 求值得到 lambda 函数或者宏。注意这不是调用函数。
//
// LAMBDA     := (lambda PARAMETERS BODY)
// MACRO      := (macro  PARAMETERS BODY)
// PARAMETERS := (PARAMETER...)
//
// PARAMETER  :: symbol
// BODY       :: expr
static struct lvalue *eval_lambda(
	struct runtime *rt,
	struct env *env,
	struct slice *vl,
	enum functype ft)
{
	if (vl->len != 3) {
		logerror(
			"lambda or macro takes 2 arguments, found: %d",
			(int)(vl->len - 1));
		return NULL;
	}

	struct lvalue *v_lambda = &vl->data[0];
	struct lvalue *v_parameters = &vl->data[1];
	struct lvalue *v_body = &vl->data[2];

	if (v_parameters->type != MC_LIST) {
		logerror("1st argument of lambda should be a list of symbols");
		return NULL;
	}

	struct lambda *lmd = (struct lambda *)malloc(sizeof(struct lambda));

	lmd->type = LAMBDA_LISP;
	lmd->env = env;
	lmd->pos = v_lambda->pos;
	lmd->parameters = (struct da_dstr)DA_INIT;

	for (usize i = 0; i < v_parameters->via.list.len; ++i) {
		struct lvalue *parameter = &v_parameters->via.list.data[i];

		if (parameter->type != MC_SYM) {
			logerror(
				"argument %d is not symbol: %s",
				i,
				VTYPE_INFO.name[parameter->type]);
			return NULL;
		}

		DA_APPEND(lmd->parameters, parameter->via.sym);
	}

	lmd->via.body = v_body;
	val_dbg("lambda body", lmd->via.body);

	struct lvalue *v;

	switch (ft) {
	case LAMBDA_LISP:
		v = new_val(rt, MC_FUNC);
		v->via.func = lmd;
		break;
	case FUNC_MACRO:
		v = new_val(rt, MC_MACRO);
		v->via.macro = lmd;
		break;
	}

	return v;
}

struct lvalue_opt {
	enum code code;
	struct lvalue *v;
};

/*
IF_FORM   := (if CONDITION THEN OTHERWISE)

CONDITION :: expr
THEN      :: expr
OTHERWISE :: expr
*/
static struct lvalue *
eval_if(struct runtime *rt, struct env *env, struct slice *vl)
{
	if (vl->len != 4) {
		logerror("if-form need 4 elements in list");
		return NULL;
	}

	struct lvalue *cond = eval(rt, env, &vl->data[1]);

	if (cond->type != MC_BOOL) {
		logerror("condition of if-form is not boolean");
		return NULL;
	}

	return eval(rt, env, &vl->data[cond->via.bool_ ? 2 : 3]);
}

/*
LET_FORM := (let BINDS... BODY)
BINDS    := (NAME EXPR)

NAME     :: symbol
EXPR     :: expr
*/
static struct lvalue *
eval_let(struct runtime *rt, struct env *env, struct slice *vl)
{
	if (vl->len < 3) {
		logerror("let-form need at least 3 elements in list");
		return NULL;
	}

	struct env *env1 = env;
	for (usize i = 1; i < vl->len - 1; ++i) {
		if (vl->data[i].type != MC_LIST) {
			return NULL;
		}

		enum code code =
			eval_bind(rt, env1, &vl->data[i].via.list, &env1);

		if (code != CODE_OK) {
			return NULL;
		}
	}

	return eval(rt, env1, &vl->data[vl->len - 1]);
}

static struct lvalue *eval_macro_call(
	struct runtime *rt,
	struct env *env,
	struct lambda *lambda,
	struct slice *arguments)
{
	struct env *env1 = new_env(lambda->env);

	if (arguments->len != lambda->parameters.len) {
		logerror(
			"argument count error, %d arguments and %d parameters",
			arguments->len,
			lambda->parameters.len);
		return NULL;
	}

	// 为 macro 的每个实参（argument）创建一个 MC_LAZY，绑定到形参。
	for (usize i = 0; i < lambda->parameters.len; ++i) {
		struct lvalue *v = new_val(rt, MC_LAZY);

		v->via.lazy->v = NULL;

		v->via.lazy->func.env = env;
		v->via.lazy->func.parameters = (struct da_dstr)DA_INIT;
		v->via.lazy->func.pos = lambda->pos;
		v->via.lazy->func.type = LAMBDA_LISP;
		v->via.lazy->func.via.body = &arguments->data[i];

		env_bind(env1, lambda->parameters.data[i], v);
	}

	struct lvalue *result;

	switch (lambda->type) {
	case LAMBDA_LISP:
		result = eval(rt, env1, lambda->via.body);
		break;
	case LAMBDA_BUILTIN:
		result = lambda->via.builtin(rt, env1);
		break;
	}

	return result;
}

static struct lvalue *eval_lambda_call(
	struct runtime *rt,
	struct env *env,
	struct lambda *lambda,
	struct slice *arguments)
{
	struct env *env1 = new_env(lambda->env);

	if (arguments->len != lambda->parameters.len) {
		logerror(
			"argument count error, %d arguments and %d parameters",
			arguments->len,
			lambda->parameters.len);
		return NULL;
	}

	for (usize i = 0; i < lambda->parameters.len; ++i) {
		struct lvalue *v = eval(rt, env, &arguments->data[i]);

		logdebug(
			"eval argument i=%d, parameter=%.*s",
			i,
			lambda->parameters.data[i].len,
			lambda->parameters.data[i].data);

		env_bind(env1, lambda->parameters.data[i], v);
	}

	struct lvalue *result;

	switch (lambda->type) {
	case LAMBDA_LISP:
		result = eval(rt, env1, lambda->via.body);
		break;
	case LAMBDA_BUILTIN:
		result = lambda->via.builtin(rt, env1);
		break;
	}

	return result;
}

static struct lvalue *
eval_list(struct runtime *rt, struct env *env, struct slice *vl)
{
	if (vl->len == 0) {
		return &VAL_NIL;
	}

	struct lvalue *head = &vl->data[0];

	if (head->type == MC_SYM) {
		if (dstr_eq(&head->via.sym, &SYM.let)) {
			return eval_let(rt, env, vl);
		}

		if (dstr_eq(&head->via.sym, &SYM.if_)) {
			return eval_if(rt, env, vl);
		}

		if (dstr_eq(&head->via.sym, &SYM.func)) {
			return eval_lambda(rt, env, vl, FUNC_LAMBDA);
		}

		if (dstr_eq(&head->via.sym, &SYM.quote)) {
			if (vl->len != 2) {
				logerror("quote takes 2 arguments");
				return NULL;
			}
			return &vl->data[1];
		}

		if (dstr_eq(&head->via.sym, &SYM.macro)) {
			return eval_lambda(rt, env, vl, FUNC_MACRO);
		}
	}

	struct lvalue *val_lambda = eval(rt, env, head);

	if (val_lambda == NULL) {
		return NULL;
	}

	struct slice args = slice_tail(vl);

	switch (val_lambda->type) {
	case MC_MACRO:
		return eval_macro_call(rt, env, val_lambda->via.func, &args);
	case MC_FUNC:
		return eval_lambda_call(rt, env, val_lambda->via.func, &args);
	default:
		logerror("leading value of list is not function");
		return NULL;
	}
}

/*
// 绑定 v 指向的代码中的所有变量。
//
// - 如果变量定义在 env 中，就替换被绑定的值。
// - 否则，如果变量定义在 parameters 中，就替换为 MC_PARAMETER 类型的 val。
// - 否则，返回错误 CODE_UNDEFINED。
static enum code
bind(struct runtime *rt,
     struct env *env,
     struct val *v,
     struct da_dstr parameters);
*/

struct lvalue *eval(struct runtime *rt, struct env *env, struct lvalue *v)
{
	DA_APPEND(rt->func_stack, v->pos);

	switch (v->type) {
	case MC_SYM:
		v = env_find(env, &v->via.sym);
		break;
	case MC_LIST:
		v = eval_list(rt, env, &v->via.list);
		break;
	case MC_LAZY:
		if (v->via.lazy->v == NULL) {
			v->via.lazy->v = eval_lambda_call(
				rt,
				env,
				&v->via.lazy->func,
				NULL);
		}

		return v->via.lazy->v;
	default:
		break;
	}

	if (v != NULL) {
		DA_POP(rt->func_stack);
	}

	return v;
}
